﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using Microsoft.Practices.Prism.ViewModel;
using System.ComponentModel.DataAnnotations;
using Tanmia.Core.Properties;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;
using System.Collections;

namespace Tanmia.Basis.Core
{
    public class BaseBusinessObject : INotifyPropertyChanged, IDataErrorInfo, IValidatable, ICloneable
    {
        #region Private Fields
        private bool isValidatable;
        private int oldValidationResultCount = -1;
        private ObjectValidator validator;
        #endregion

        public BaseBusinessObject()
        {
            isValidatable = false;
            validator = new ObjectValidator(this);
        }

        #region INotifyPropertyChanged Members

        public virtual event PropertyChangedEventHandler PropertyChanged;

        [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "Method used to raise an event")]
        protected virtual void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "Method used to raise an event")]
        protected virtual void RaisePropertyChanged(params string[] propertyNames)
        {
            if (propertyNames == null) throw new ArgumentNullException("propertyNames");

            foreach (var name in propertyNames)
            {
                this.RaisePropertyChanged(name);
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "Method used to raise an event")]
        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Cannot change the signature")]
        protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
        {
            var propertyName = PropertySupport.ExtractPropertyName(propertyExpression);
            this.RaisePropertyChanged(propertyName);
        }

        protected virtual void SetValue<T>(string propertyName, ref T variable, T value)
        {
            if (variable == null || !variable.Equals(value))
            {
                variable = value;
                RaisePropertyChanged(propertyName);
            }
        }

        #endregion

        #region IDataErrorInfo Members

        public virtual string Error
        {
            get
            {
                if (!isValidatable)
                    return null;

                return validator.Error;
            }
        }

        public virtual string this[string columnName]
        {
            get
            {
                if (!isValidatable)
                    return null;

                RaisePropertyChanged(() => Error);

                return validator[columnName];
            }
        }

        #endregion

        #region IValidatable Members

        public virtual bool IsValid()
        {
            return string.IsNullOrEmpty(validator.Error);
        }

        public virtual ICollection<ValidationResult> Validate()
        {
            string tmp = validator.Error;
            ICollection<ValidationResult> retVals = validator.ValidationResults ?? new List<ValidationResult>();

            if (oldValidationResultCount != retVals.Count)
            {
                RaisePropertyChanged(() => Error);
                oldValidationResultCount = retVals.Count;
            }
            isValidatable = true;

            return retVals;
        }

        #endregion

        #region ICloneable Members

        public virtual object Clone()
        {
            object newObj = Activator.CreateInstance(GetType());
            object stepValue = null;
            object stepValue1 = null;

            var properties = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(x => x.CanRead && x.CanWrite);

            foreach (PropertyInfo propInfo in properties)
            {
                stepValue = propInfo.GetValue(this, null);

                if (stepValue is BaseBusinessObject)
                    propInfo.SetValue(newObj, ((BaseBusinessObject)stepValue).Clone(), null);
                else if (stepValue is IEnumerable && !(stepValue is string))
                {
                    stepValue1 = propInfo.GetValue(newObj, null);
                    foreach (object item in (IEnumerable)stepValue)
                        ((IList)stepValue1).Add(((BaseBusinessObject)item).Clone());
                }
                else
                    propInfo.SetValue(newObj, stepValue, null);
            }

            return newObj;
        }

        #endregion

        //public override bool Equals(object obj)
        //{
        //    if (obj == null) return false;

        //    if (GetType() != obj.GetType()) return false;

        //    return true;
        //}
    }
}
